# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::VulnerabilityScanning::FindingMap, feature_category: :software_composition_analysis do
  let_it_be(:identifiers) { (1...25).map { build(:ci_reports_security_identifier, external_id: SecureRandom.uuid) } }
  let_it_be(:report_finding) { build(:ci_reports_security_finding, identifiers: identifiers) }
  let_it_be(:expected_identifiers) { identifiers.first(Vulnerabilities::Finding::MAX_NUMBER_OF_IDENTIFIERS) }
  let_it_be(:ignored_identifiers) { identifiers.drop(Vulnerabilities::Finding::MAX_NUMBER_OF_IDENTIFIERS) }

  let_it_be(:scanner) do
    build(:vulnerabilities_scanner,
      external_id: Gitlab::VulnerabilityScanning::SecurityScanner::EXTERNAL_ID,
      name: Gitlab::VulnerabilityScanning::SecurityScanner::NAME,
      vendor: Gitlab::VulnerabilityScanning::SecurityScanner::VENDOR)
  end

  let(:identifiers_map) do
    {}.tap do |h|
      expected_identifiers.each_with_index do |ident, index|
        h[ident.fingerprint] = index + 1
      end
    end
  end

  let(:finding_map) { described_class.new(report_finding, scanner.id) }

  describe '#uuid' do
    subject { finding_map }

    it { is_expected.to delegate_method(:uuid).to(:report_finding) }
  end

  describe '#severity' do
    subject { finding_map }

    it { is_expected.to delegate_method(:severity).to(:report_finding) }
  end

  describe '#evidence' do
    subject { finding_map }

    it { is_expected.to delegate_method(:evidence).to(:report_finding) }
  end

  describe '#identifiers' do
    subject { finding_map.identifiers }

    before do
      allow(Gitlab::AppJsonLogger).to receive(:warn).and_call_original
      finding_map.set_identifier_ids_by(identifiers_map)
    end

    it 'takes only up to the max identifiers limit' do
      expect(subject).to eq(expected_identifiers)
      expect(Gitlab::AppJsonLogger).to have_received(:warn)
        .with(
          message: 'Finding exceeded max number of allowed identifiers - excess identifiers ignored',
          ignored_identifiers: ignored_identifiers
        )
    end
  end

  describe '#set_identifier_ids_by' do
    let(:sequencer) { ::Ingestion::Sequencer.new }

    subject { finding_map.set_identifier_ids_by(identifiers_map) }

    before do
      allow(finding_map).to receive(:set_identifier_ids_by).and_wrap_original do |_, fingerprint_id_map|
        fingerprint_id_map.map { sequencer.next }
      end
    end

    it 'changes the identifier_ids of the finding_map' do
      expect(finding_map.identifier_ids).to match_array(sequencer.range)
    end
  end

  describe '#to_hash' do
    before do
      finding_map.set_identifier_ids_by(identifiers_map)
    end

    let(:expected_hash) do
      {
        uuid: report_finding.uuid,
        scanner_id: scanner.id,
        primary_identifier_id: 1,
        location_fingerprint: report_finding.location.fingerprint,
        project_fingerprint: report_finding.project_fingerprint,
        name: 'Cipher with no integrity',
        report_type: :sast,
        severity: :high,
        confidence: :medium,
        metadata_version: 'sast:1.0',
        details: {},
        raw_metadata: report_finding.raw_metadata,
        description: 'The cipher does not provide data integrity update 1',
        solution: 'GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.',
        cve: report_finding.cve,
        location: {
          "class" => "com.gitlab.security_products.tests.App",
          "end_line" => 29,
          "file" => "maven/src/main/java/com/gitlab/security_products/tests/App.java",
          "method" => "insecureCypher",
          "start_line" => 29
        }
      }
    end

    subject { finding_map.to_hash }

    it { is_expected.to eq(expected_hash) }
  end
end
